#include <ArduinoJson.h>//json文件库
#include <AsyncTCP.h> //网路传输协议：异步TCP
#include <ESPAsyncWebServer.h>//网络服务器，与WIFI.h连用
#include <WiFi.h>
#include <SPIFFS.h>//闪存文件库，与FS.h连用
#include <FS.h> 
#include <LiquidCrystal.h>//OLED库函数
#include <DallasTemperature.h>//DS18B20库函数，单总线协议，与OneWire.h连用
#include <OneWire.h>
#include "I2Cdev.h"//I2C设备驱动
#include "MPU6050.h"//陀螺仪库函数
//#include <Wire.h>//arduino I2C协议，下面有预定义
#include "MAX30105.h"//血氧库
#include "heartRate.h"//心率库

#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif

#define SERVERWIFISSID "coat"//AP模式  WIFI名
#define SERVERWIFIPWD "0123456789"//wifi密码

#define VERTREFRESH_GYRO 5//n次每秒，陀螺仪刷新率
#define VERTREFRESH_TEMP 1//n次每秒
#define WARNINGPIN 19//蜂鸣器
#define WARMPIN 16
//#define OUTPUT_READABLE_ACCELGYRO//输出陀螺仪实时数据

double nowTump = 0;//设备当前的温度
double setTump = 0;//设置的温度
bool updataHR_spo2 = false;//血氧饱和度
int addHR = 0 , addSPO2 = 0;

AsyncWebServer server(80);//创建web服务器端口号

#define ONE_WIRE_BUS 5    //定义单总线连接的端口
OneWire oneWire(ONE_WIRE_BUS);//定义单总线对象oneWire
DallasTemperature sensors(&oneWire);//定义DS18B20对象sensors

MPU6050 accelgyro;//定义陀螺仪对象accelgyro

MAX30105 particleSensor;//定义血氧心率对象particleSensor

#define MAX_BRIGHTNESS 255

int16_t ax , ay , az;//角加速度，加速度计陀螺仪原始数据
int16_t gx , gy , gz;//加速度

const byte RATE_SIZE = 4; //心率样本数量
byte rates[RATE_SIZE]; //心率样本数组
byte rateSpot = 0;
long lastBeat = 0; //最后的采样时间

float beatsPerMinute;
int beatAvg = -1;
float spo2 = -1;

bool wraing_ = false;
void setup()
{
	Serial.begin(115200);//开启串口打印，波特率115200

	pinMode(WARNINGPIN , OUTPUT);//蜂鸣器引脚输出
	pinMode(WARMPIN , OUTPUT);
	for ( size_t i = 0; i < 3; i++ )
	{
		Serial.println("打开加热");
		digitalWrite(WARMPIN , LOW);
		Serial.println(digitalRead(WARMPIN));
		delay(1000);
		Serial.println("关闭加热");
		digitalWrite(WARMPIN , HIGH);
		Serial.println(digitalRead(WARMPIN));
		delay(1000);
	}
	
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
	Wire.begin();
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
	Fastwire::setup(400 , true);
#endif

	if ( !particleSensor.begin(Wire , I2C_SPEED_FAST) ) //检测心率传感器是否连接正常
	{
		Serial.println(F("MAX30105 was not found. Please check wiring/power."));
		while ( 1 );
	}
	particleSensor.setup(); //初始化心率传感器
	particleSensor.setPulseAmplitudeRed(0x0A);//将红色LED调低，表示传感器正在运行
	particleSensor.setPulseAmplitudeGreen(0); //关闭绿灯

	WiFi.mode(WIFI_MODE_AP);
	WiFi.softAP(SERVERWIFISSID , SERVERWIFIPWD);

	server.on("/update" , HTTP_GET , updataTump);//更新请求
	server.begin();//开启web服务器 
	Serial.println("开启web服务器 ");
	sensors.begin();//启动单总线
	accelgyro.initialize();//陀螺仪初始化
	Serial.println(accelgyro.testConnection() ? "MPU6050 connection successful" : "MPU6050 connection failed");
	if ( !accelgyro.testConnection() )
	{
		while ( true )
		{
			delay(10);
		}
	}

	xTaskCreatePinnedToCore(                        //创建双核任务
		refresh_gyro                                  //创建任务，刷新陀螺仪数据
		, "runData_tack"                              //创建任务的名称
		, 1024 * 2                                    //分配的运行空间大小，堆栈大小，以字节为单位
		, NULL                                        //任务句柄，任务输入参数
		, 2                                           //任务优先级，0为最低级
		, NULL                                        //回调函数句柄
		, 1);                                         //将任务运行的内核ID，0或1
}
//初始化结束
void refresh_gyro(void* pvParameters)
{

	while ( true )
	{
		accelgyro.getMotion6(&ax , &ay , &az , &gx , &gy , &gz);

	#ifdef OUTPUT_READABLE_ACCELGYRO
		// display tab-separated accel/gyro x/y/z values
		Serial.print("a/g:\t");
		Serial.print(ax); Serial.print("\t");
		Serial.print(ay); Serial.print("\t");
		Serial.print(az); Serial.print("\t");
		Serial.print(gx); Serial.print("\t");
		Serial.print(gy); Serial.print("\t");
		Serial.println(gz);
	#endif
		if ( ay > -9300)
		{
			Serial.println("可能跌倒");
			digitalWrite(WARNINGPIN , HIGH);
			wraing_ = true;
			vTaskDelay(1000);
		}
		if ( gy < -12000 )
		{
			Serial.println("可能已经起来");
			wraing_ = false;
			digitalWrite(WARNINGPIN , LOW);
			vTaskDelay(1000);
		}
		vTaskDelay(1000 / VERTREFRESH_GYRO);
	}
}

void updataTump(AsyncWebServerRequest* request)
{
	double setTump_t = request->arg("setT").toDouble();
	if ( setTump != setTump_t )
	{
		setTump = setTump_t;
		Serial.print("更新数据为：");
		Serial.println(setTump);
	} else
	{
		Serial.println("数据不需要更新");
	}
	String outData = "";
	StaticJsonDocument<192> doc;//声明一个大小为192字节的JsonDocument对象doc

	JsonArray hr = doc.createNestedArray("hr");
	hr.add(1);
	hr.add(beatAvg);

	JsonArray o2 = doc.createNestedArray("o2");
	o2.add(1);
	o2.add(spo2);

	JsonArray temp = doc.createNestedArray("temp");
	temp.add(nowTump);
	temp.add(wraing_);

	serializeJson(doc , outData);//直接把表单中的数据通过name属性进行解析，转换成JSON格式
	request->send(200 , "text/plain" , outData);
}

void getData()
{
	long irValue;
	int errNum = 0;
	for ( size_t j = 0; j < 5; j++ )
	{
		if ( particleSensor.getIR() < 50000 )
		{
			Serial.println("不要移开手指");
			Serial.println(errNum);
			delay(1000);
			errNum++;
			j = 0;
			if ( errNum > 3 )
			{
				Serial.println("数据采集失败!");
				return;
			}
			continue;
		}
		for ( byte i = 0; i < 100; i++ )
		{
			irValue = particleSensor.getIR();

			if ( checkForBeat(irValue) == true )
			{
				//We sensed a beat!
				long delta = millis() - lastBeat;
				lastBeat = millis();

				beatsPerMinute = 60 / ( delta / 1000.0 );

				if ( beatsPerMinute < 255 && beatsPerMinute > 20 )
				{
					rates[rateSpot++] = (byte) beatsPerMinute; //将此读数存储在读数的平均数组中  
					rateSpot %= RATE_SIZE;

					//取平均读数
					beatAvg = 0;
					for ( byte x = 0; x < RATE_SIZE; x++ )
						beatAvg += rates[x];
					beatAvg /= RATE_SIZE;
				}
			}
		}
		Serial.print("已采集");
		Serial.print(j + 1);
		Serial.println("/5个样本");
		if ( j == 4 )
		{
			if ( 40 < beatAvg && beatAvg < 100 )
			{
				Serial.print("血氧：");
				spo2 = irValue / 1000.0;
				if ( spo2 > 100 )
				{
					Serial.print("100%");
				} else
				{
					Serial.print(spo2);
					Serial.print("%");
				}
				Serial.print("心率：");
				Serial.print(beatAvg);
				Serial.print("/min");
			} else
			{
				Serial.print("数据无效，请重新采集@");
				Serial.println(beatAvg);
			}
		}
	}
}

void loop()
{
	sensors.requestTemperatures();    //发送温度测量请求命令
	nowTump = sensors.getTempCByIndex(0);//获取指定设备序号0的温度值
	Serial.println(nowTump);
	if ( setTump > nowTump )
	{
		//打开加热
		Serial.println("打开加热");
		digitalWrite(WARMPIN , LOW);
		Serial.println(digitalRead(WARMPIN));
	} else
	{
		Serial.println("关闭加热");
		digitalWrite(WARMPIN , HIGH);
	}
	delay(1000 / VERTREFRESH_TEMP);
	delay(5000);
	getData();
}
